﻿/**
 * \file CStaticMemPool.h
 * \reversion 0.1
 * \brief 静态内存池，应用于小内存分配的静态内存池
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include "CStaticMemPool.h"

/**
 * \brief 构造函数
 */
CStaticMemPool::CStaticMemPool()
{
	m_pCurPool = NULL;
	size_t pos = 0;

	this->InitMemPool();
}

/**
 * \brief 析构函数
 */
CStaticMemPool::~CStaticMemPool()
{
	this->PrintMemBlock();

	/// 释放
	//MemPool* pPool = this->m_headPool._pNext;
	//while(pPool)
	//{
	//	if(pPool->_pBeg)
	//	{
	//		free(pPool->_pBeg);
	//	}
	//	MemPool* tmp = pPool;
	//	pPool = pPool->_pNext;
	//	free(tmp);
	//}

	//if(this->m_headPool._pBeg)
	//{
	//	free(this->m_headPool._pBeg);
	//}
}

/**
 * \brief 初始化内存池
 */
void CStaticMemPool::InitMemPool()
{
	/// 初始化分配内存池
	this->m_headPool._pBeg = static_cast<char*>(malloc(__INIT_POOL_SIZE_));
	if(!this->m_headPool._pBeg)
	{
		/// 打日志
		return;
	}

	/// 空闲内存开始地址
	this->m_headPool._pFreeBeg = this->m_headPool._pBeg;
	/// 内存结束地址
	this->m_headPool._pEnd = this->m_headPool._pBeg;
	this->m_headPool._pEnd += __INIT_POOL_SIZE_;
	this->m_headPool._pNext = NULL;

	/// 设置当前内存池
	this->m_pCurPool = &(this->m_headPool);

	/// 初始化内存块
	size_t pos = 0;
	for(; pos < __NFREELISTS_NUM_; ++pos)
	{
		this->Refill(pos * __ALIGN_ + __ALIGN_);
	}

#ifdef _HT_DEBUG
	std::cout << "[CStaticMemPool] 初始化内存池：addr=" << static_cast<void*>(this->m_headPool._pFreeBeg) << ", size=" << __INIT_POOL_SIZE_ << std::endl;
#endif
}

/**
 * \brief 上调到8的倍数
 * \param _bytes：位数
 * \return 返回大小
 */
size_t CStaticMemPool::RoundUp(size_t _bytes) 
{
	return (((_bytes) + (size_t) __ALIGN_ - 1) & ~((size_t) __ALIGN_ - 1));
}

/**
 * \brief 根据块大小，计算块索引号
 * \param _bytes：位数
 * \return 索引号
 */
size_t CStaticMemPool::FreeListIndex(size_t _bytes)
{
	return (((_bytes) + (size_t)__ALIGN_ - 1) / (size_t)__ALIGN_ - 1);
}

/**
 * \brief 申请一段内存
 * \param _size：申请的大小
 * \return 内存首地址
 */
void* CStaticMemPool::Allocate(size_t _size)
{
	if(!_size || _size > __MAX_BYTES_)
	{
		return NULL;
	}

	_size = this->RoundUp(_size);
	size_t idx = this->FreeListIndex(_size);

	/// 链表中没有可用内存块
	if(!this->m_FreeList[idx]._pHead)
	{
		/// 用内存池填充
		if(!this->Refill(_size))
		{
			return NULL;
		}
	}

	if(this->m_FreeList[idx]._pHead)
	{
		/// 得到一个内存块
		void* ret = this->m_FreeList[idx]._pHead->_ClientData;
		/// 移动内存块链表的头指针
		this->m_FreeList[idx]._pHead = this->m_FreeList[idx]._pHead->_pFreeListLink;
		/// std::cout << "Allocate8:" << _size << std::endl;

		/// 引用计数
		++(this->m_FreeList[idx]._count);
		return ret;
	}

	return NULL;
}

/**
 * \brief 释放一段内存
 * \param p：内存指针
 * \param _size：大小
 * \return 是否成功
 */
bool CStaticMemPool::Deallocate(void *p, size_t _size)
{
	if(!_size || _size > __MAX_BYTES_)
	{
		std::cout << "[CStaticMemPool] Deallocate error size:" << _size << std::endl;
		return false;
	}

	if(p)
	{
		/// 长度转换
		_size = this->RoundUp(_size);
		size_t idx = this->FreeListIndex(_size);

		MemObj* pInsert = static_cast<MemObj*>(p);
		pInsert->_pFreeListLink = this->m_FreeList[idx]._pHead;
		this->m_FreeList[idx]._pHead = pInsert;

		/// 引用计数
		--(this->m_FreeList[idx]._count);

		return true;
	}

	return false;
}

/**
 * \brief 填充块
 * \param _size：要填充的块大小
 * \return 是否成功
 */
bool CStaticMemPool::Refill(size_t _size)
{
	_size = this->RoundUp(_size);

	size_t idx = this->FreeListIndex(_size);

	size_t pos = 0;
	for(; pos < __FILL_NUM_; ++pos)
	{
		/// 判断内存池中内存是否足够分配
		if(__MEM_POOL_FREE_SIZE(this->m_pCurPool) < _size)
		{
			break;
		}

		/// 从内存池中申请一块内存
		MemObj* pInsert = (MemObj*)static_cast<void*>(this->m_pCurPool->_pFreeBeg);
		this->m_pCurPool->_pFreeBeg += _size;

		/// 把申请好的内存链入内存块表
		pInsert->_pFreeListLink = this->m_FreeList[idx]._pHead;
		this->m_FreeList[idx]._pHead = pInsert;
	}

	/// 如果分配至少一个内存块，则填充完毕
	if(pos)
	{
		return true;
	}

	/// 以_size的两倍对应的内存块开始查找空闲内存块
	size_t multiple = 2;
	while(_size * multiple <= __MAX_BYTES_)
	{
		/// 被查找的块内存大小
		size_t blockMemSize = _size * multiple;

		/// 得到块索引
		size_t blockIdx = this->FreeListIndex(blockMemSize);
		/// 得到第一块内存
		char* pBlockMem = (char*)static_cast<void*>(this->m_FreeList[blockIdx]._pHead);
		if(pBlockMem)
		{
			/// 把第一块内存弹出内存块链表
			this->m_FreeList[blockIdx]._pHead = this->m_FreeList[blockIdx]._pHead->_pFreeListLink;

			/// 分割并填充内存
			while(blockMemSize)
			{
				/// 新内存插入到头结点
				MemObj* pInsert = (MemObj*)static_cast<void*>(pBlockMem);
				pInsert->_pFreeListLink = this->m_FreeList[idx]._pHead;
				this->m_FreeList[idx]._pHead = pInsert;

				/// 内存偏移_size
				pBlockMem += _size;

				/// 填充内存的大小减少
				if(blockMemSize >= _size)
				{
					blockMemSize -= _size;
				}
				else
				{
					break;
				}
			}

			return true;
		}
		/// 倍数增加
		++multiple;
	}

	/// 内存块中也没有可填充内存，则分配新的内存池

	/// 分配之前要把老的内存池的内存填充到内存块中
	/// 得到当前内存池中的内存大小
	size_t poolSize = __MEM_POOL_FREE_SIZE(this->m_pCurPool);

	/// poolSize应该是__ALIGN_的倍数，如果不是，保留这个碎片，打出错误日志
	if(poolSize % __ALIGN_ != 0)
	{
#ifdef _HT_DEBUG
		std::cout << "[CStaticMemPool] Refill时内存池出现碎片，size=" << poolSize % __ALIGN_ << std::endl;
#endif
		poolSize = RoundUp(poolSize) - __ALIGN_;
	}

	/// 内存池里还有剩余时
	if(poolSize)
	{
		/// 把内存插入相应内存块链表的头结点
		size_t blockIdx = this->FreeListIndex(poolSize);
		MemObj* pInsert = (MemObj*)static_cast<void*>(this->m_pCurPool->_pFreeBeg);
		pInsert->_pFreeListLink = this->m_FreeList[blockIdx]._pHead;
		this->m_FreeList[blockIdx]._pHead = pInsert;
	}

	/// 新建内存池结构
	this->m_pCurPool->_pNext = static_cast<MemPool*>(malloc(sizeof(MemPool)));
	if(!this->m_pCurPool->_pNext)
	{
#ifdef _HT_DEBUG
		std::cout << "[CStaticMemPool] Refill时malloc(sizeof(MemPool))失败" << std::endl;
#endif
		/// ...
		return false;
	}

	/// 移动当前内存池指针
	this->m_pCurPool = this->m_pCurPool->_pNext;

	/// 新建内存池
	this->m_pCurPool->_pBeg = static_cast<char*>(malloc(__INIT_POOL_SIZE_));
	if(!this->m_pCurPool->_pBeg)
	{
#ifdef _HT_DEBUG
		std::cout << "[CStaticMemPool] Refill时malloc(__INIT_POOL_SIZE_)失败" << std::endl;
#endif
		return false;
	}
	this->m_pCurPool->_pFreeBeg = this->m_pCurPool->_pBeg;
	this->m_pCurPool->_pEnd = this->m_pCurPool->_pBeg + __INIT_POOL_SIZE_;
	this->m_pCurPool->_pNext = NULL;
#ifdef _HT_DEBUG
	std::cout << "[CStaticMemPool] 新建立内存池：addr=" << static_cast<void*>(this->m_pCurPool->_pBeg) << ", size=" << __INIT_POOL_SIZE_ << std::endl;
#endif

	/// 再次找到索引块
	idx = this->FreeListIndex(_size);

	/// 再次分配内存
	pos = 0;
	for(; pos < __FILL_NUM_; ++pos)
	{
		/// 判断内存池中内存是否足够分配
		if(__MEM_POOL_FREE_SIZE(this->m_pCurPool) < _size)
		{
			break;
		}

		/// 从内存池中申请一块内存
		MemObj* pInsert = (MemObj*)static_cast<void*>(this->m_pCurPool->_pFreeBeg);
		this->m_pCurPool->_pFreeBeg += _size;

		/// 把申请好的内存链入内存块表
		pInsert->_pFreeListLink = this->m_FreeList[idx]._pHead;
		this->m_FreeList[idx]._pHead = pInsert;
	}

	if(pos)
	{
		return true;
	}

#ifdef _HT_DEBUG
	std::cout << "[CStaticMemPool] Refill失败" << std::endl;
#endif
	return false;
}

/**
 * \brief 打印各个内存块的使用情况，关机程序时，打印可查询内存泄露
 */
void CStaticMemPool::PrintMemBlock()
{
	std::cout << "【CStaticMemPool_MemLookUp】内存使用信息：" << std::endl;
	size_t pos = 0;
	size_t size = 0;
	for(; pos < __NFREELISTS_NUM_; ++pos)
	{
		std::cout << "	[" << pos * __ALIGN_ + __ALIGN_ << "] 未释放内存数量：" << this->m_FreeList[pos]._count << std::endl;
		MemObj* pMem = this->m_FreeList[pos]._pHead;
		while(pMem)
		{
			size += pos * __ALIGN_ + __ALIGN_;
			pMem = pMem->_pFreeListLink;
		}
	}

	MemPool* pMem = this->m_pCurPool;
	while(pMem)
	{
		size += pMem->_pEnd - pMem->_pFreeBeg;
		pMem = pMem->_pNext;
	}

	size_t poolSize = 0;

	pMem = &this->m_headPool;
	while(pMem)
	{
		poolSize += pMem->_pEnd - pMem->_pBeg;
		pMem = pMem->_pNext;
	}

	std::cout << "【CStaticMemPool_MemLookUp】内存池中内存数量为：" << poolSize << "，内存块与内存池中空闲内存总量为：" << size << std::endl;
}
